<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h2>一、axios是什么</h2>
  <div>axios 是一个轻量的 HTTP客户端

    基于 XMLHttpRequest 服务来执行 HTTP 请求，支持丰富的配置，支持 Promise，支持浏览器端和 Node.js 端。</div>
  <h3>特性</h3>
  <ul>
    <li>从浏览器中创建 XMLHttpRequests</li>
    <li>从 node.js 创建 http请求</li>
    <li>支持 Promise API</li>
    <li>拦截请求和响应</li>
    <li>转换请求数据和响应数据</li>
    <li>取消请求</li>
    <li>自动转换JSON 数据</li>
    <li>客户端支持防御XSRF</li>
  </ul>
  <h3>基本使用</h3>
  <script>
    // 安装
    // 项目中安装
    // npm install axios --S
    // cdn 引入
    // <script src="https://unpkg.com/axios/dist/axios.min.js">

    // 导入
    // import axios from 'axios'

    // 发送请求
    // axios({        
    //   url:'xxx',    // 设置请求的地址
    //   method:"GET", // 设置请求方法
    //   params:{      // get请求使用params进行参数凭借,如果是post请求用data
    //     type: '',
    //     page: 1
    //   }
    // }).then(res => {  
    //   // res为后端返回的数据
    //   console.log(res);   
    // })

    // 并发请求axios.all([])
    // function getUserAccount() {
    //     return axios.get('/user/12345');
    // }

    // function getUserPermissions() {
    //     return axios.get('/user/12345/permissions');
    // }

    // axios.all([getUserAccount(), getUserPermissions()])
    //     .then(axios.spread(function (res1, res2) { 
    //     // res1第一个请求的返回的内容，res2第二个请求返回的内容
    //     // 两个请求都执行完成才会执行
    // }));
  </script>
  <h2>二、为什么要封装</h2>
  <div>如果每发起一次HTTP请求，就要把这些比如设置超时时间、设置请求头、根据项目环境判断使用哪个请求地址、错误处理等等操作，都需要写一遍

    这种重复劳动不仅浪费时间，而且让代码变得冗余不堪，难以维护。为了提高我们的代码质量，我们应该在项目中二次封装一下 axios 再使用</div>
  <script>
    // 如果每个页面都发送类似的请求，都要写一堆的配置与错误处理，就显得过于繁琐了这时候我们就需要对axios进行二次封装，让使用更为便利
    // axios('http://localhost:3000/data', {
    //   // 配置代码
    //   method: 'GET',
    //   timeout: 1000,
    //   withCredentials: true,
    //   headers: {
    //     'Content-Type': 'application/json',
    //     Authorization: 'xxx',
    //   },
    //   transformRequest: [function (data, headers) {
    //     return data;
    //   }],
    //   // 其他请求配置...
    // })
    // .then((data) => {
    //   // todo: 真正业务逻辑代码
    //   console.log(data);
    // }, (err) => {
    //   // 错误处理代码  
    //   if (err.response.status === 401) {
    //   // handle authorization error
    //   }
    //   if (err.response.status === 403) {
    //   // handle server forbidden error
    //   }
    //   // 其他错误处理.....
    //   console.log(err);
    // });
  </script>
  <h2>三、如何封装</h2>
  <div>封装的同时，你需要和 后端协商好一些约定，请求头，状态码，请求超时时间.......</div>
  <br>
  <ul>
    <li>请求头 :  来实现一些具体的业务，必须携带一些参数才可以请求(例如：会员业务)</li>
    <li>状态码:   根据接口返回的不同status ， 来执行不同的业务，这块需要和后端约定好</li>
    <li>请求方法：根据get、post等方法进行一个再次封装，使用起来更为方便</li>
    <li>请求拦截器:  根据请求的请求头设定，来决定哪些请求可以访问</li>
    <li>响应拦截器：这块就是根据 后端`返回来的状态码判定执行不同业务</li>
  </ul>
  <h3>设置接口请求前缀</h3>
  <div>利用node环境变量来作判断，用来区分开发、测试、生产环境</div>
  <br>
  <div>在本地调试的时候，还需要在vue.config.js文件中配置devServer实现代理转发，从而实现跨域</div>
  <script>
    // 利用node环境变量来作判断，用来区分开发、测试、生产环境
    // if (process.env.NODE_ENV === 'development') {
    //   axios.defaults.baseURL = 'http://dev.xxx.com'
    // } else if (process.env.NODE_ENV === 'production') {
    //   axios.defaults.baseURL = 'http://prod.xxx.com'
    // }

    // 在本地调试的时候，还需要在vue.config.js文件中配置devServer实现代理转发，从而实现跨域
    // devServer: {
    //   proxy: {
    //     '/proxyApi': {
    //       target: 'http://dev.xxx.com',
    //       changeOrigin: true,
    //       pathRewrite: {
    //         '/proxyApi': ''
    //       }
    //     }
    //   }
    // }
  </script>
  <h3>设置请求头与超时时间</h3>
  <div>大部分情况下，请求头都是固定的，只有少部分情况下，会需要一些特殊的请求头，这里将普适性的请求头作为基础配置。当需要特殊请求头时，将特殊请求头作为参数传入，覆盖基础配置</div>
  <script>
    // const service = axios.create({
    //   ...
    //   timeout: 30000,  // 请求 30s 超时
    //   headers: {
    //         get: {
    //           'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
    //           // 在开发中，一般还需要单点登录或者其他功能的通用请求头，可以一并配置进来
    //         },
    //         post: {
    //           'Content-Type': 'application/json;charset=utf-8'
    //           // 在开发中，一般还需要单点登录或者其他功能的通用请求头，可以一并配置进来
    //         }
    //   },
    // })
  </script>
  <h3>封装请求方法</h3>
  <div>先引入封装好的方法，在要调用的接口重新封装成一个方法暴露出去</div>
  <script>
    // get 请求
    // export function httpGet({
    //   url,
    //   params = {}
    // }) {
    //   return new Promise((resolve, reject) => {
    //     axios.get(url, {
    //       params
    //     }).then((res) => {
    //       resolve(res.data)
    //     }).catch(err => {
    //       reject(err)
    //     })
    //   })
    // }

    // // post
    // // post请求
    // export function httpPost({
    //   url,
    //   data = {},
    //   params = {}
    // }) {
    //   return new Promise((resolve, reject) => {
    //     axios({
    //       url,
    //       method: 'post',
    //       transformRequest: [function (data) {
    //         let ret = ''
    //         for (let it in data) {
    //           ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
    //         }
    //         return ret
    //       }],
    //       // 发送的数据
    //       data,
    //       // url参数
    //       params

    //     }).then(res => {
    //       resolve(res.data)
    //     })
    //   })
    // }
  </script>
  <br>
  <div>把封装的方法放在一个api.js文件中</div>
  <script>
    // import { httpGet, httpPost } from './http'
    // export const getorglist = (params = {}) => httpGet({ url: 'apps/api/org/list', params })
  </script>
  <br>
  <div>页面中就能直接调用</div>
  <script>
    // .vue
    // import { getorglist } from '@/assets/js/api'

    // getorglist({ id: 200 }).then(res => {
    //   console.log(res)
    // })
  </script>
  <br>
  <div>这样可以把api统一管理起来，以后维护修改只需要在api.js文件操作即可</div>
  <h3>请求拦截器</h3>
  <div>请求拦截器可以在每个请求里加上token，做了统一处理后维护起来也方便</div>
  <script>
    // 请求拦截器
    // axios.interceptors.request.use(
    //   config => {
    //     // 每次发送请求之前判断是否存在token
    //     // 如果存在，则统一在http请求的header都加上token，这样后台根据token判断你的登录情况，此处token一般是用户完成登录后储存到localstorage里的
    //     token && (config.headers.Authorization = token)
    //     return config
    //   },
    //   error => {
    //     return Promise.error(error)
    //   })
  </script>
  <h3>响应拦截器</h3>
  <div>响应拦截器可以在接收到响应后先做一层操作，如根据状态码判断登录状态、授权</div>
  <script>
    // 响应拦截器
    // axios.interceptors.response.use(response => {
    //   // 如果返回的状态码为200，说明接口请求成功，可以正常拿到数据
    //   // 否则的话抛出错误
    //     if (response.status === 200) {
    //       if (response.data.code === 511) {
    //         // 未授权调取授权接口
    //       } else if (response.data.code === 510) {
    //         // 未登录跳转登录页
    //       } else {
    //         return Promise.resolve(response)
    //       }
    //     } else {
    //       return Promise.reject(response)
    //     }
    //   }, error => {
    //     // 我们可以在这里对异常状态作统一处理
    //     if (error.response.status) {
    //       // 处理请求失败的情况
    //       // 对不同返回码对相应处理
    //       return Promise.reject(error.response)
    //     }
    //   })
  </script>
  <h3>小结</h3>
  <ul>
    <li>封装是编程中很有意义的手段，简单的axios封装，就可以让我们可以领略到它的魅力</li>
    <li>封装 axios 没有一个绝对的标准，只要你的封装可以满足你的项目需求，并且用起来方便，那就是一个好的封装方案</li>
  </ul>
</body>
</html>